package cn.gjxblog.transaction;

import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * 测试事务生效案例
 */
public class NoTransactionTests {

    AnnotationConfigApplicationContext context;
    @Before
    public void before(){
        context = new AnnotationConfigApplicationContext();
        context.register(AnnotationTransactionTests.RootConfig.class, NoTransactionTestsConfig.class);
        context.refresh();
    }


    /**
     * 案例一结果：
     * test()     事务失效
     * testCopy() 事务生效
     */
    @Test
    public void test(){
        UserService userService = context.getBean(UserService.class);
        userService.a();
    }

    @Test
    public void testCopy(){
        UserServiceCopy userService = context.getBean(UserServiceCopy.class);
        userService.a();
    }


    /**
     * 案例二结果：
     * test2() 事务生效
     * test2Copy() 事务不生效
     */
    @Test
    public void test2(){
        UserService2 userService = context.getBean(UserService2.class);
        userService.a();
    }
    @Test
    public void test2Copy(){
        UserService2Copy userService = context.getBean(UserService2Copy.class);
        userService.a();
    }

    /**
     * 案例三结果：事务生效
     */
    @Test
    public void test3(){
        UserService3 userService = context.getBean(UserService3.class);
        userService.a();
    }

    /**
     * 案例四结果：事务生效
     */
    @Test
    public void test4(){
        UserService4 userService = context.getBean(UserService4.class);
        userService.a();
    }





    /**
     * 案例 一
     * 事务并没有回滚
     *
     * 入口a()，因为a()没有标注注解，所以AOP没有生效，没有生成代理类。内部this调用本类方法b()，所以b的事务也没有生效
     */
    static class UserService{
        @Autowired
        DataSource dataSource;


        public void a(){
            b();
        }

        @Transactional
        public void b()  {
            insertDB(dataSource);
            int i = 10/0;
        }
    }

    /**
     * 将UserService改造一下
     * 注入自己，通过自己调用b方法，这时虽然a方法没有被AOP切到，但是b方法被AOP切中了，也能使事务生效。
     */
    static class UserServiceCopy{
        @Autowired
        DataSource dataSource;

        @Autowired
        UserService userService;

        public void a(){
            userService.b();
        }

        @Transactional
        public void b()  {
            insertDB(dataSource);
            int i = 10/0;
        }
    }

    /**
     *案例二
     *
     * 此时b方法事务并没有起任何作用
     * 事务回滚的原理是：
     *  b抛出异常，a方法跟着继续抛出异常，通过a方法上的事务完成了回滚
     */
    static class UserService2{
        @Autowired
        DataSource dataSource;

        @Transactional()
        public void a(){
            b();
        }

        @Transactional()
        public void b()  {
            insertDB(dataSource);
            int i = 10/0;
        }
    }

    /**
     * 对上面的例子进行改造，说明b上的事务并没有生效
     * 让a方法不对ArithmeticException此类异常回滚，验证结果。
     * 结果数据正常入库，说明最终生效的是a上的事务，b方法此时并无事务。
     */
    static class UserService2Copy{
        @Autowired
        DataSource dataSource;

        @Transactional(noRollbackFor = {ArithmeticException.class})
        public void a(){
            b();
        }

        @Transactional()
        public void b()  {
            insertDB(dataSource);
            int i = 10/0;
        }
    }


    /**
     * 案例三
     *
     * a()方法自身抛出异常，a()方法事务捕获到异常完成了回滚
     */
    static class UserService3{
        @Autowired
        DataSource dataSource;

        @Transactional
        public void a(){
            b();
            int i = 10/0;
        }

        public void b()  {
            insertDB(dataSource);
        }
    }


    /**
     * 案例4  案例二本质上就是案例四
     * a()方法自身抛出异常，a()方法事务捕获到异常完成了回滚
     */
    static class UserService4{
        @Autowired
        DataSource dataSource;

        @Transactional
        public void a(){
            b();
        }

        public void b()  {
            insertDB(dataSource);
            int i = 10/0;

        }
    }


    public static void insertDB(DataSource dataSource){
        try {
            Statement statement = DataSourceUtils.getConnection(dataSource).createStatement();
            statement.execute("insert into user (`name`,`age`) values ('Mack',22)");
        } catch (SQLException e) {
        }
    }

    @Configuration
    @EnableTransactionManagement
    static class NoTransactionTestsConfig{
        @Bean
        public UserService userService(){
            return new UserService();
        }
        @Bean
        public UserServiceCopy UserServiceCopy(){
            return new UserServiceCopy();
        }
        @Bean
        public UserService2 userService2(){
            return new UserService2();
        }
        @Bean
        public UserService2Copy userService2Copy(){
            return new UserService2Copy();
        }
        @Bean
        public UserService3 userService3(){
            return new UserService3();
        }
        @Bean
        public UserService4 userService4(){
            return new UserService4();
        }
    }

}
